package org.maggioni.springsandbox.rehearsals.e05aop.e01basic.aspects;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.maggioni.springsandbox.rehearsals.e05aop.e01basic.model.BusinessObject;

@Aspect
public class LoggingAspect {

	@Pointcut("execution(* get*())")
	public void allGetters() {}

	@Before("within(org.maggioni.springsandbox.rehearsals.e05aop.e01basic.services.BusinessService)")
	public void loggingAdviceForEveryMethodInBusinessService(JoinPoint joinPoint) {
		System.out.println("(@Before) "+joinPoint.toString());
	}

	@Before("allGetters()")
	// Use && to call the advice on more pointcuts. Example: @Before("allGetters() && allSetters()")
	public void loggingAdviceWithPointcut() {
		System.out.println("(@Before) A logging advice with pointcut (allGetters) is called");
	}
	
	@After("execution(* set*(*)) && args(parameter)")
	public void loggindAdviceForAllSetters(JoinPoint joinPoint,String parameter) {
		System.out.println("(@After) A setter is called with param " + joinPoint.getArgs()[0] + " (" + parameter + ")");
	}
	
	@After("@annotation(org.maggioni.springsandbox.rehearsals.e05aop.e01basic.annotations.Loggable)")
	public void loggindAdviceForLoggableAnnotations() {
		System.out.println("(@After) A method with annotation 'Loggable' has been called");
	}
	
	@Before("execution(String get*())")
	public void loggingAdviceForStringOnly() {
		System.out.println("(@Before) A String getter is called.");
	}

	@After("allGetters()")
	public void loggingAdviceForShapeServiceAfter() {
		System.out.println("(@After) A logging advice with pointcut (allGetters) is called");
	}
	
	@AfterReturning("execution(* doThrow(..))")
	public void loggingAdviceReturning() {
		System.out.println("(@AfterReturning) function doThrow completed without throwing");
	}

	@AfterReturning(pointcut="execution(* doThrow(..))",returning="returnBusinessObject")
	public void loggingAdviceReturningAndModifyReturn(BusinessObject returnBusinessObject) {
		returnBusinessObject.setField1(returnBusinessObject.getField1()+" - modified");
		System.out.println("(@AfterReturning) doThrow has modified the output value!");
	}

	@AfterThrowing(pointcut="execution(* doThrow(..))",throwing="ex")
	// (..) = zero or more parameters
	// (*)  = one or more parameters
	// ()   = no parameters
	public void loggingAdviceThrowing(Exception ex) {
		System.out.println("(@AfterThrowing) doThrow ("+ex.getClass().getName()+")");
	}

	@Before("args(arg)")
	public void loggingAdviceByArgs(String arg) {
		System.out.println("(@Before) A logging advice by args (String) is called with parameter "+arg);
	}

	@Before("args(arg)")
	public void loggingAdviceByArgs(JoinPoint joinPoint, int arg) {
		System.out.println("(@Before) A logging advice by args (int) is called with parameter "+arg+", join point: "+joinPoint.getSignature());
	}

	@Around("execution(* doThrowForAroundAdvice(..))")
	public Object loggingAdviceForAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
		BusinessObject ret = null;
		try {
			System.out.println("(@Around) Before advice");
			ret = (BusinessObject) proceedingJoinPoint.proceed();
			ret.setField1( ret.getField1()+" - modified by around advice" );
			System.out.println("(@Around) After returning");
		} catch (Throwable e) {
			System.out.println("(@Around) After throwing");
			throw e;
		}
		System.out.println("(@Around) After finally");
		return ret;
	}
	

}
